package com.zzyl.nursing.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.constant.Constants;
import com.zzyl.nursing.domain.AlertData;
import com.zzyl.nursing.domain.AlertRule;
import com.zzyl.nursing.domain.DeviceData;
import com.zzyl.nursing.mapper.AlertRuleMapper;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.service.IAlertDataService;
import com.zzyl.nursing.service.IAlertRuleService;
import com.zzyl.system.mapper.SysRoleMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 报警规则Service业务层处理
 * 
 * @author cisimu
 * @date 2025-10-28
 */
@Service
@Slf4j
public class AlertRuleServiceImpl extends ServiceImpl<AlertRuleMapper, AlertRule> implements IAlertRuleService
{
    @Autowired
    private AlertRuleMapper alertRuleMapper;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private DeviceMapper deviceMapper;
    @Autowired
    private SysRoleMapper roleMapper;
    @Autowired
    private IAlertDataService alertDataService;

    @Value("${alertRule.roleRepair}")
    private String roleRepair;    //修理工

    @Value("${alertRule.roleAdmin}")
    private String roleAdmin;    //超级管理员

    /**
     * 查询报警规则
     * 
     * @param id 报警规则主键
     * @return 报警规则
     */
    @Override
    public AlertRule selectAlertRuleById(Long id)
    {
        return getById(id);
    }

    /**
     * 查询报警规则列表
     * 
     * @param alertRule 报警规则
     * @return 报警规则
     */
    @Override
    public List<AlertRule> selectAlertRuleList(AlertRule alertRule)
    {
        return alertRuleMapper.selectAlertRuleList(alertRule);
    }

    /**
     * 新增报警规则
     * 
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int insertAlertRule(AlertRule alertRule)
    {
        return save(alertRule) ? 1 : 0;
    }

    /**
     * 修改报警规则
     * 
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int updateAlertRule(AlertRule alertRule)
    {
        return updateById(alertRule) ? 1 : 0;
    }

    /**
     * 批量删除报警规则
     * 
     * @param ids 需要删除的报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleByIds(Long[] ids)
    {
        return removeByIds(Arrays.asList(ids)) ? 1 : 0;
    }

    /**
     * 删除报警规则信息
     * 
     * @param id 报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleById(Long id)
    {
        return removeById(id) ? 1 : 0;
    }


    /**
     * 过滤报警规则，把符合要求的数据存入报警数据表
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void alertRuleFilter() {
        //1.查询所有规则
        Long count = this.lambdaQuery().eq(AlertRule::getStatus, Constants.ALERT_RULE_STATUS_UNLOCK).count();
        if(count == 0){
            log.error("[报警规则校验]没有查询到任何报警规则，不需处理");
            return;
        }
        //2.查询所有设备上报的数据
        List<Object> valueList = redisTemplate.opsForHash().values(CacheConstants.IOT_DEVICE_LAST_DATA);
        if(CollUtil.isEmpty(valueList)){
            log.error("[报警规则校验]没有查询到任何设备上报数据");
            return;
        }
        //3.将设备上报数据格式化
        List<DeviceData> deviceDataAllList = new ArrayList<>();
        for (Object object : valueList) {
            String deviceDataListJSON = (String) object;
            List<DeviceData> deviceDataList = JSONUtil.toList(deviceDataListJSON, DeviceData.class);
            deviceDataAllList.addAll(deviceDataList);
        }
        //4.遍历设备上报数据
        for (DeviceData deviceData : deviceDataAllList) {
            //前置校验，如果设备上报的数据是很久之前的，就可以忽略，不处理
            Integer timeout = 3600;  //单位：秒，超过一小时，就忽略
            LocalDateTime now = LocalDateTime.now();
            LocalDateTime alarmTime = deviceData.getAlarmTime();
            long between = Math.abs(LocalDateTimeUtil.between(alarmTime, now, ChronoUnit.SECONDS));
            if(between > timeout){
                log.error("[报警规则校验]设备名称：{}，功能id：{}，设备id：{}，设备上报数据时间太久，已超过{}秒，不再处理",deviceData.getDeviceName(),deviceData.getFunctionId(),deviceData.getIotId(),timeout);
                continue;
            }
            //4.1 查询设备关联的报警规则数据
            //4.1.1 查询上报数据设备同产品的其他设备关联的报警规则
            List<AlertRule> alertRuleListForProduct = this.lambdaQuery()
                    .eq(AlertRule::getProductKey, deviceData.getProductKey())
                    .eq(AlertRule::getIotId, -1)
                    .eq(AlertRule::getFunctionId,deviceData.getFunctionId())
                    .eq(AlertRule::getStatus, Constants.ALERT_RULE_STATUS_UNLOCK)
                    .list();

            //4.1.2 查询上报数据设备关联的报警规则
            List<AlertRule> alertRuleListForDevice = this.lambdaQuery()
                    .eq(AlertRule::getProductKey,deviceData.getProductKey())
                    .eq(AlertRule::getIotId, deviceData.getIotId())
                    .eq(AlertRule::getFunctionId,deviceData.getFunctionId())
                    .eq(AlertRule::getStatus, Constants.ALERT_RULE_STATUS_UNLOCK)
                    .list();
            //4.1.3 合并规则列表并去重
            Set<AlertRule> alertRuleListAll = CollUtil.unionDistinct(alertRuleListForDevice, alertRuleListForProduct);
            if(CollUtil.isEmpty(alertRuleListAll)){
                log.error("[报警规则校验]设备名称：{}，设备ID：{}，功能id：{}，设备关联的报警规则没有找到",deviceData.getDeviceName(),deviceData.getIotId(),deviceData.getFunctionId());
                continue;
            }
            //4.2 遍历报警规则数据
            for (AlertRule alertRule : alertRuleListAll) {
                //4.2.1 第一层校验：判断是否在规则生效时间内
                String alertEffectivePeriod = alertRule.getAlertEffectivePeriod();
                List<String> list = Arrays.asList(alertEffectivePeriod.split("~"));
                LocalTime startTime = LocalTime.parse(list.get(0));
                LocalTime endTime = LocalTime.parse(list.get(1));
                LocalTime deviceAlarmTime = deviceData.getAlarmTime().toLocalTime();
                if(deviceAlarmTime.isBefore(startTime) || deviceAlarmTime.isAfter(endTime)){
                    log.error("[报警规则校验]设备名称：{}，设备ID：{}，功能id：{}，规则id：{}，规则名称：{}，设备上报数据时间不在报警规则生效时间段内",deviceData.getDeviceName(),deviceData.getIotId(),deviceData.getFunctionId(),alertRule.getAlertRuleName(),alertRule.getId());
                    continue;
                }
                //4.2.2 第二层校验：判断数据是否达到阈值
                String countKey = CharSequenceUtil.format(CacheConstants.IOT_ALERT_COUNT_KEY, deviceData.getIotId(),deviceData.getFunctionId(),alertRule.getId());
                double deviceValue = Double.parseDouble(deviceData.getDataValue());
                Double value = alertRule.getValue();
                String operator = alertRule.getOperator();
                if((operator.equals("<") && deviceValue > value) || (operator.equals(">=") && deviceValue < value)){
                    log.error("[报警规则校验]设备名称：{}，设备id：{}，功能：{}，规则名：{}，现值：{}，阈值：{}，设备上传数据没有达到报警规则的阈值",deviceData.getDeviceName(),deviceData.getIotId(),alertRule.getFunctionName(),alertRule.getAlertRuleName(),deviceData.getDataValue(),alertRule.getValue());
                    //删除持续周期,例如前两次报警符合要求，第三次不符合，就删除持续周期，清空，重新开始
                    redisTemplate.delete(countKey);
                    continue;
                }
                //4.2.3 第三层校验：判断沉默周期
                String silentKey = CharSequenceUtil.format(CacheConstants.IOT_ALERT_SILENT_KEY, deviceData.getIotId(),deviceData.getFunctionId(),alertRule.getId());
                Long silentTime = redisTemplate.getExpire(silentKey);   //获取silentKEy的剩余时间，即沉默周期
                String silentValue = redisTemplate.opsForValue().get(silentKey);
                if(StrUtil.isNotBlank(silentValue)){
                    log.error("[报警规则校验]设备名称：{}，设备id：{}，功能：{}，规则名：{}，还在沉默周期内，无需处理，沉默周期还剩{}秒",deviceData.getDeviceName(),deviceData.getIotId(),alertRule.getFunctionName(),alertRule.getAlertRuleName(),silentTime);
                    continue;
                }
                //4.2.4 第四层校验：判断持续周期
                Integer duration = alertRule.getDuration();   //持续周期最大值

                Long durationCount = redisTemplate.opsForValue().increment(countKey); //次数按1自增
                if(durationCount < duration){
                    log.error("[报警规则校验]设备名称：{}，设备id：{}，功能：{}，规则名：{},还在持续周期内，无需处理,现次数：{}，上限次数：{}",deviceData.getDeviceName(),deviceData.getIotId(),alertRule.getFunctionName(),alertRule.getAlertRuleName(),durationCount,duration);
                    continue;
                }

                //保存沉默周期到缓存
                Integer alertSilentPeriod = alertRule.getAlertSilentPeriod();  //沉默周期时间
                redisTemplate.opsForValue().set(silentKey, "silentTime", alertSilentPeriod, TimeUnit.MINUTES);

                //删除持续周期
                redisTemplate.delete(countKey);

                //4.2.5 报警规则校验通过，则处理数据后新增报警数据
                //4.2.5.1 查询护理员或维修员id列表
                List<Long> nursingIdList = new ArrayList<>();
                if (alertRule.getAlertDataType() == 0){
                    //如果是老人数据
                    if(deviceData.getLocationType() == 0){
                        //随身设备
                        //根据老人关联查询护理员id
                        nursingIdList = deviceMapper.selectNursingIdsByMoveIotId(deviceData.getIotId());
                    }else{
                        //固定设备
                        //根据床位关联查询护理员id
                        nursingIdList = deviceMapper.selectNursingIdsByStopIotId(deviceData.getIotId());
                    }
                }else{
                    //如果是设备数据,查询维修员
                   nursingIdList = roleMapper.selectUserIdsByUserRule(roleRepair);
                }
                //4.2.5.2 查询管理员id列表
                List<Long> adminUserIdsList = roleMapper.selectUserIdsByUserRule(roleAdmin);
                //合并用户id
                Set<Long> userIdList = CollUtil.unionDistinct(nursingIdList, adminUserIdsList);
                if(CollUtil.isEmpty(userIdList)){
                    log.error("[报警规则校验]设备id：{}，没有查询到用户id",deviceData.getIotId());
                }
                //4.2.5.3 构建报警数据
                AlertData baseData = BeanUtil.toBean(deviceData, AlertData.class);
                baseData.setId(null);
                baseData.setAlertRuleId(alertRule.getId());
                baseData.setStatus(0);
                baseData.setType(alertRule.getAlertDataType());
                //报警原因，格式：功能名称+运算符+阈值+持续周期+沉默周期
                String alertReasonTemplate = "报警原因：{}{}{}，持续周期：{}，沉默周期：{}";
                String alertReason = CharSequenceUtil.format(alertReasonTemplate, alertRule.getFunctionName(), alertRule.getOperator(), alertRule.getValue(), alertRule.getDuration(), alertRule.getAlertSilentPeriod());
                baseData.setAlertReason(alertReason);
                //遍历用户ID
                List<AlertData> alertDataList = userIdList.stream().map(userId -> {
                    AlertData alertData = BeanUtil.toBean(baseData, AlertData.class);
                    alertData.setUserId(userId);
                    return alertData;
                }).collect(Collectors.toList());
                //4.2.5.4 新增报警数据
                alertDataService.saveBatch(alertDataList);
                log.info("[报警规则校验]设备id：{}，报警信息：{}，批量添加报警数据完毕",deviceData.getIotId(),alertReason);
            }
        }
    }
}
